from authlib.jose import jwt
from flask import Flask
from flask_jwt import JWT, jwt_required, current_identity
from werkzeug.security import safe_str_cmp
'''
session 的局限性
基于 Session 的验证过程大体是：服务器端有一个 Session 词典，当用户验证登录后，在词典中为该用户创建一个 Session 对象，
在响应（ response ）中返回一个 Session id，当用户下次请求时，携带 Session id，服务器从 Session 词典中可以恢复出 Session 对象，以完成用户的验证，在用 Session id 从恢复出认证实体。

从 Session 验证过程可以看出一些局限性：

服务器横向扩展很困难：因为 Session 只能存活在一个服务实例中，将用户请求引导到其他服务器，将丢掉用户的登录状态
携带信息量少，恢复会话信息比较耗时：Session 认证后，客户端得到 Session ID, 服务器无法从 Session ID 中得到更多信息，需要从数据库、文件系统或缓存中取得用户信息，比较耗时
没有统一标准：Session 由各个服务器框架自己实现，没有统一标准，存在应用扩展困难的问题，特别加密方式，五花八门，有很大的安全隐患

token 简介
为了解决 Session 的问题，有了 token 的验证方式。
token 可以理解成票据，或者凭证，当用户得到服务器的认证后，由服务器颁发，在之后的请求时携带，免去频繁登录。

token 不同于 Session 的地方：

可以独立于具体的服务器框架生成和校验
可以携带更多的信息，避免对持久层的查询操作
基于标准的算法可以由不同的节点完成验证
为了利用好 token 的验证机制，IEIT (互联网工程任务组)，制定了基于 JSON 数据结构的网络认证方式 JWA（JSON Web Algorithms），
还针对不同应用场景提出了具体协议，如 JWS、JWE、JWK 等，他们可以统称为 JWT，即 Javascript Web Token。
'''
# 阮一峰jwt入门教程：https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

# header = {'alg': 'HS256'}
# payload = {'iss': 'Authlib', 'sub': '123', 'name': 'bob'}
# secret = '123abc.'
# token = jwt.encode(header, payload, secret)
# # print(token)
# # 解码
# claims = jwt.decode(token, secret)
# print(claims.header)

# 可借助postman测试 /auth 获取jwt token   /protected 请求headers参数{"Authorization":"jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODY3Njk4ODEsImlhdCI6MTU4Njc2OTU4MSwibmJmIjoxNTg2NzY5NTgxLCJpZGVudGl0eSI6MX0.ZOFS-yBu9kT24E_8ZSAC49B12dHPlL-zR7jRdVa2dHU"}
 # User 类，用于模拟用户实体
class User(object):
    def __init__(self, id, username, password):
        self.id = id
        self.username = username
        self.password = password

    def __str__(self):
        return "User(id='%s')" % self.id

# User 实体集合，用于模拟用户对象的缓存
users = [
    User(1, 'user1', 'abcxyz'),
    User(2, 'user2', 'abcxyz'),
]

username_table = {u.username: u for u in users}
userid_table = {u.id: u for u in users}

# 获取认证的回调函数，从 request 中得到登录凭证，返回凭证所代表的 用户实体
def authenticate(username, password):
    user = username_table.get(username, None)
    if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):
        return user

# 通过 token 获得认证主体的回调函数
def identity(payload):
    user_id = payload['identity']
    return userid_table.get(user_id, None)

app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'super-secret'

jwt = JWT(app, authenticate, identity)  # 用 JWT 初始化应用

@app.route('/protected', methods= ["GET", "POST"])  # 定义一个 endpoint
@jwt_required()  # 声明需要 token 才能访问
def protected():
    return '%s' % current_identity  # 验证通过返回 认证主体

if __name__ == '__main__':
    app.run()
